﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Reflection;
using System.Security.Policy;
using System.Security.Permissions;
namespace K.EnterpriseLibary
{
    public static class ReflectionPublic
    {
        const string DllFileExtention = ".DLL";
        const string FullGenericTypeNameFormat = "{0}`1[[{2}]],{1}";
        const string GenericTypeNameFormat = "{0}`1[[{1}]]";
        const string PublicKey = "0024000004800000940000000602000000240000525341310004000001000100A916C4AF57C7EEF9F7249D648F1F9DA4A8E8AD818FB32E5617CDEFCFC367EEAEAC4CCF4924EE9DC5019CDA75D832A7D784E0E6DEDD8955410C4EF17AC9CD840ED55210833CDBCD95D9F77539AFD13F0A81DBBF89CFCCE1C9D5AF39D1006F288CF450AC5BEA94424F29D8A79C1B6BC99FDD459E2881BDCA5C80FA0BA45CFB7DCB";

        private static List<Assembly> _GlobalAssemblyList = new List<Assembly>();
        private static List<AddonInfo> _GlobalAddonTypeList = new List<AddonInfo>();

        public static List<Assembly> GlobalAssemblyList
        {
            get
            {
                return _GlobalAssemblyList;
            }
        }

        public static List<AddonInfo> GlobalAddonTypeList
        {
            get
            {
                return _GlobalAddonTypeList;
            }
        }

        public static void Initialize(IEnumerable<Assembly> assemblyList)
        {
            if (assemblyList != null)
                _GlobalAssemblyList.AddRange(assemblyList);
        }

        public static void Initialize(string directoryPath)
        {
            LoadAssembliesFromDirectory(
                _GlobalAssemblyList
                , _GlobalAddonTypeList
                , directoryPath
                , true
                , null);
        }

        #region 读取Assembly

        public static Assembly LoadAssembly(string fileName)
        {
            AssemblyName assemblyName = AssemblyName.GetAssemblyName(fileName);
            return LoadAssembly(assemblyName);
        }

        public static Assembly LoadAssembly(AssemblyName assemblyName)
        {
            Evidence asEvidence = AppDomain.CurrentDomain.Evidence;
            return AppDomain.CurrentDomain.Load(assemblyName, asEvidence);
        }

        /// <summary>
        /// 從目錄中讀取文件
        /// </summary>
        public static void LoadAssembliesFromDirectory(
            List<Assembly> assemblyList
            , List<AddonInfo> addonTypeList
            , string directoryPath
            , bool searchSubdirectories
            , Predicate<string> fileFilterMethod)
        {
            string[] files = System.IO.Directory.GetFiles(directoryPath);
            foreach (string fileName in files)
            {
                // 確認文件是否符合查詢條件
                if ((fileFilterMethod == null || fileFilterMethod(fileName))
                    && fileName.ToUpper().EndsWith(DllFileExtention))
                {
                    AssemblyName assemblyName = AssemblyName.GetAssemblyName(fileName);
                    if (!assemblyList.Any(p => p.FullName == assemblyName.FullName)
                        && assemblyName.GetPublicKey().BinaryToString() == PublicKey)
                    {
                        Assembly assembly = LoadAssembly(assemblyName);

                        if (assembly != null)
                        {
                            assemblyList.Add(assembly);
                        }
                    }
                }
            }

            // 搜索子目錄
            if (searchSubdirectories)
            {
                string[] subdirectories = System.IO.Directory.GetDirectories(directoryPath);
                foreach (string subdirectory in subdirectories)
                {
                    LoadAssembliesFromDirectory(assemblyList
                        , addonTypeList
                        , subdirectory
                        , true
                        , fileFilterMethod);
                }
            }

            // 添加所有的AddonType
            List<Type> addonList = FindTypes(assemblyList
                , p => p.HasAttribute<AddonAttribute>(false));
            foreach (Type addonType in addonList)
            {
                // 確保AddonInfo不重複
                AddonInfo addonInfo = new AddonInfo(addonType);
                if (!addonTypeList.Any(p => p.ID == addonInfo.ID))
                    addonTypeList.Add(addonInfo);
            }
        }

        #endregion

        #region 类型操作
        /// <summary>
        /// 获取对象的属性(Propery)值或索引值
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="myObject">对象</param>
        /// <param name="propertyName">属性(Propery)值</param>
        /// <returns>对象的制定的属性(Propery)的值</returns>
        public static T GetPropertyOrIndexValue<T>(this object myObject, string propertyName)
        {
            object displayValue;
            if (myObject.IsNull())
                return default(T);

            // 首先通過索引器獲取值
            Type dataType = myObject.GetType();
            PropertyInfo thisMember = dataType.GetProperty("Item", new Type[] { typeof(string) });
            if (!thisMember.IsNull())
            {
                displayValue = thisMember.GetValue(myObject
                    , new object[] { propertyName });
            }
            else
            {
                displayValue = myObject.GetPropertyValue<T>(propertyName);
            }

            return displayValue.Cast<T>();
        }

        /// <summary>
        /// 获取对象的属性(Propery)值
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="myObject">对象</param>
        /// <param name="propertyName">属性(Propery)值</param>
        /// <returns>对象的制定的属性(Propery)的值</returns>
        public static T GetPropertyValue<T>(this object myObject, string propertyName)
        {
            myObject.CheckNull("Object cannot be null.");

            Type objectType = myObject.GetType();
            PropertyInfo propertyInfo = objectType.GetProperty(propertyName);
            if (propertyInfo == null)
                throw new KException("Property dose not belong to type"
                    , "PropertyName", propertyName
                    , "ObjectTypeName", objectType.FullName);

            return propertyInfo.GetValue(myObject, null).Cast<T>();
        }

        public static bool IsNumberType(this Type type)
        {
            return type.IsType<sbyte>()
                || type.IsType<byte>()
                || type.IsType<short>()
                || type.IsType<ushort>()
                || type.IsType<int>()
                || type.IsType<uint>()
                || type.IsType<long>()
                || type.IsType<ulong>()
                || type.IsType<decimal>()
                || type.IsType<float>()
                || type.IsType<double>();
        }
        #endregion

        #region 檢查

        /// <summary>
        /// 檢查類是否是某個類的子類
        /// </summary>
        public static void CheckBaseType<T>(this Type childType, string errorMessage, params object[] extendParameters)
        {
            System.Diagnostics.Debug.Assert(childType != null);
            childType.IsSubclassOf(typeof(T)).CheckTrue(errorMessage
                , extendParameters.Concat(
                    "ChildType", childType.FullName
                    , "BaseType", typeof(T).FullName));
        }

        /// <summary>
        /// 檢查類是否是某個類的子類
        /// </summary>
        public static void CheckBaseType<T>(this Type childType)
        {
            CheckBaseType<T>(childType, "Type is not a child.");
        }

        /// <summary>
        /// 檢查對象是否為某個類別
        /// </summary>
        /// <param name="testObject"></param>
        /// <param name="message"></param>
        public static void CheckType<T>(this Object testObject, string message, params object[] extendParameters)
        {
            (testObject is T).CheckTrue(message
                , extendParameters.Concat(
                    "SourceType", testObject == null ? "Unkown" : testObject.GetType().AssemblyQualifiedName
                    , "DestType", typeof(T).AssemblyQualifiedName));
        }

        /// <summary>
        /// 檢查對象是否為某個類別
        /// </summary>
        /// <param name="testObject"></param>
        /// <param name="message"></param>
        public static void CheckType<T>(this Object testObject)
        {
            testObject.CheckType<T>("Object type is not matched.");
        }

        #endregion

        #region 特性
        public static bool HasAttribute(this MemberInfo memberInfo, Type attributeType, bool inherit)
        {
            Attribute[] attributes = (Attribute[])memberInfo.GetCustomAttributes(attributeType, inherit);
            if (attributes.IsNullOrEmpty() || attributes.Length == 0)
                return false;
            return true;
        }

        public static Attribute GetAttribute(this MemberInfo memberInfo, Type attributeType, bool inherit)
        {
            Attribute[] attributes = (Attribute[])memberInfo.GetCustomAttributes(attributeType, inherit);
            if (attributes.IsNullOrEmpty() || attributes.Length == 0)
                return null;
            return attributes[0];
        }

        public static bool HasAttribute<T>(this MemberInfo memberInfo, bool inherit)
            where T : Attribute
        {
            return HasAttribute(memberInfo, typeof(T), inherit);
        }

        public static T GetAttribute<T>(this MemberInfo memberInfo, bool inherit)
            where T : Attribute
        {
            return (T)GetAttribute(memberInfo, typeof(T), inherit);
        }
        #endregion 特性

        #region 类型特性判断

        /// <summary>
        /// 類別是否擁有某個特性
        /// </summary>
        /// <param name="type">類別</param>
        /// <param name="parameters">參數數組,第一個參數就是特性的類別</param>
        /// <returns>如果類別擁有某個特性,返回true,否則返回false.</returns>
        public static bool AttributeInType(this Type type, Type attributeType)
        {
            if (type.GetCustomAttributes(attributeType, false).Length == 0)
                return false;
            else
                return true;
        }

        /// <summary>
        /// 是否是指定類別
        /// </summary>
        /// <param name="type">類別</param>
        /// <param name="parameters">參數數組,第一個參數就是指定類別</param>
        /// <returns>如果是指定類別,返回true,否則返回false.</returns>
        public static bool IsType(this Type type, Type baseType)
        {
            if (type == baseType)
                return true;
            else
                return false;
        }

        /// <summary>
        /// 是否是指定類別的子類
        /// </summary>
        /// <param name="type">類別</param>
        /// <param name="parameters">參數數組,第一個參數就是指定類別</param>
        /// <returns>如果是指定類別的子類,返回true,否則返回false.</returns>
        public static bool IsSubClass(this Type type, Type baseType)
        {
            if (type.IsSubclassOf(baseType))
                return true;
            else
                return false;
        }

        /// <summary>
        /// 類別是否實現了某個接口
        /// </summary>
        /// <param name="type">類別</param>
        /// <param name="parameters">參數數組,第一個參數就是指定接口</param>
        /// <returns>如果是否實現了某個接口,返回true,否則返回false.</returns>
        public static bool FindInterface(this Type type, Type interfaceType)
        {
            if (type.GetInterface(interfaceType.FullName) == null)
                return false;
            else
                return true;
        }

        /// <summary>
        /// 類別是否擁有某個特性
        /// </summary>
        /// <param name="type">類別</param>
        /// <typeparam name="T">指定特性類別</typeparam>
        /// <returns>如果類別擁有某個特性,返回true,否則返回false.</returns>
        public static bool AttributeInType<T>(this Type type) where T : Attribute
        {
            if (type.GetCustomAttributes(typeof(T), false).Length == 0)
                return false;
            else
                return true;
        }

        /// <summary>
        /// 是否是指定類別
        /// </summary>
        /// <param name="type">類別</param>
        /// <typeparam name="T">指定類別</typeparam>
        /// <returns>如果是指定類別,返回true,否則返回false.</returns>
        public static bool IsType<T>(this Type type)
        {
            if (type == typeof(T))
                return true;
            else
                return false;
        }


        /// <summary>
        /// 是否是指定類別的子類
        /// </summary>
        /// <param name="type">類別</param>
        /// <typeparam name="T">指定類別</typeparam>
        /// <returns>如果是指定類別的子類,返回true,否則返回false.</returns>
        public static bool IsSubClass<T>(this Type type)
        {
            if (type.IsSubclassOf(typeof(T)))
                return true;
            else
                return false;
        }

        /// <summary>
        /// 類別是否實現了某個接口
        /// </summary>
        /// <param name="type">類別</param>
        /// <typeparam name="T">接口類別</typeparam>
        /// <returns>如果是否實現了某個接口,返回true,否則返回false.</returns>
        public static bool FindInterface<T>(this Type type)
        {
            if (type.GetInterface(typeof(T).FullName) == null)
                return false;
            else
                return true;
        }

        /// <summary>
        /// 是否从父类继承
        /// </summary>
        public static bool IsDerivedFrom(this Type childType, Type parentType)
        {
            return childType == parentType || childType.IsSubclassOf(parentType);
        }

        /// <summary>
        /// 检查是否从父类继承
        /// </summary>
        public static void CheckTypeDerivedFrom(this Type childType, Type parentType)
        {
            System.Diagnostics.Debug.Assert(childType != null);
            System.Diagnostics.Debug.Assert(parentType != null);
            if (!IsDerivedFrom(childType, parentType))
                throw new KException("Check Type Derived From Error"
                    , "ThisTypeName", childType.FullName
                    , "ParentTypeName", parentType.FullName);
        }
        #endregion 类型特性判断

        #region 类型查找

        /// <summary>
        /// 獲取類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="findMethod">查找條件</param>
        /// <returns>類別列表</returns>
        public static List<Type> FindTypes(
            this IEnumerable<Assembly> assemblyList,
            Predicate<Assembly> assemblyFindMethod
            , Predicate<Type> typeFindMethod)
        {
            System.Diagnostics.Debug.Assert(assemblyList != null);

            List<Type> typeList = new List<Type>();
            foreach (Assembly assembly in assemblyList)
            {
                if (assemblyFindMethod == null || assemblyFindMethod(assembly))
                {
                    Type[] types = assembly.GetTypes();

                    foreach (Type type in types)
                    {
                        if (typeFindMethod == null || typeFindMethod(type))
                        {
                            typeList.Add(type);
                        }
                    }
                }
            }
            return typeList;
        }

        /// <summary>
        /// 獲取類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="findMethod">查找條件</param>
        /// <returns>類別列表</returns>
        public static List<Type> FindTypes(this IEnumerable<Assembly> assemblyList
            , Predicate<Type> findMethod)
        {
            return assemblyList.FindTypes(p => true
                , findMethod);
        }

        /// <summary>
        /// 在程序集列表中查找指定名称的类
        /// </summary>
        /// <param name="assemblyList"></param>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public static Type FindTypeByName(this IEnumerable<Assembly> assemblyList, string typeName)
        {
            foreach (Assembly assembly in assemblyList)
            {
                Type type = assembly.GetTypes().FirstOrDefault(p => p.FullName == typeName);
                if (type != null)
                    return type;
            }
            return null;
        }

        /// <summary>
        /// 根據特性在程序集中查找類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="attributeType">特性類別</param>
        /// <returns>找到的類別列表</returns>
        public static List<Type> FindTypesByAttribute(this IEnumerable<Assembly> assemblyList, Type attributeType)
        {
            return assemblyList.FindTypes(p => AttributeInType(p, attributeType));
        }

        /// <summary> 
        /// 根據類別在程序集中查找類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="myType">指定類別</param>
        /// <returns>找到的類別列表</returns>
        public static List<Type> FindTypesByType(this IEnumerable<Assembly> assemblyList, Type myType)
        {
            return assemblyList.FindTypes(p => IsType(p, myType));
        }

        /// <summary>
        /// 查找某個類別的子類
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="baseClassType">指定類別</param>
        /// <returns>找到的子類列表</returns>
        public static List<Type> FindTypesByBaseClass(this IEnumerable<Assembly> assemblyList, Type baseClassType)
        {
            return assemblyList.FindTypes(p => IsSubClass(p, baseClassType));
        }

        /// <summary>
        /// 查找實現了某個接口的的類
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="interfaceType">接口類別</param>
        /// <returns>實現了某個接口的的類列表</returns>
        public static List<Type> FindTypesByInterface(this IEnumerable<Assembly> assemblyList, Type interfaceType)
        {
            return assemblyList.FindTypes(p => FindInterface(p, interfaceType));
        }
        #endregion

        #region 在GlobalAssemblyList中的类型查找

        /// <summary>
        /// 獲取類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="findMethod">查找條件</param>
        /// <returns>類別列表</returns>
        public static List<Type> FindTypes(
            Predicate<Assembly> assemblyFindMethod
            , Predicate<Type> typeFindMethod)
        {
            return _GlobalAssemblyList.FindTypes(assemblyFindMethod, typeFindMethod);
        }

        /// <summary>
        /// 獲取類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="findMethod">查找條件</param>
        /// <returns>類別列表</returns>
        public static List<Type> FindTypes(Predicate<Type> findMethod)
        {
            return _GlobalAssemblyList.FindTypes(findMethod);
        }

        /// <summary>
        /// 獲取類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="findMethod">查找條件</param>
        /// <returns>類別列表</returns>
        public static List<Type> FindAddonTypes(Predicate<AddonInfo> findMethod)
        {
            List<Type> addonTypeList = new List<Type>();
            foreach (AddonInfo addonInfo in _GlobalAddonTypeList)
            {
                if (!findMethod.IsNull() && findMethod(addonInfo))
                    addonTypeList.Add(addonInfo.Type);
            }
            return addonTypeList;
        }

        public static List<Type> FindTypeByAddonID(Guid id)
        {
            return FindAddonTypes(p => p.ID == id);
        }

        /// <summary>
        /// 在程序集列表中查找指定名称的类
        /// </summary>
        /// <param name="assemblyList"></param>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public static Type FindTypeByName(string typeName)
        {
            return _GlobalAssemblyList.FindTypeByName(typeName);
        }

        /// <summary>
        /// 根據特性在程序集中查找類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="attributeType">特性類別</param>
        /// <returns>找到的類別列表</returns>
        public static List<Type> FindTypesByAttribute(Type attributeType)
        {
            return _GlobalAssemblyList.FindTypesByAttribute(attributeType);
        }

        /// <summary>
        /// 根據類別在程序集中查找類別
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="myType">指定類別</param>
        /// <returns>找到的類別列表</returns>
        public static List<Type> FindTypesByType(Type myType)
        {
            return _GlobalAssemblyList.FindTypesByType(myType);
        }

        /// <summary>
        /// 查找某個類別的子類
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="baseClassType">指定類別</param>
        /// <returns>找到的子類列表</returns>
        public static List<Type> FindTypesByBaseClass(Type baseClassType)
        {
            return _GlobalAssemblyList.FindTypesByBaseClass(baseClassType);
        }

        /// <summary>
        /// 查找實現了某個接口的的類
        /// </summary>
        /// <param name="assemblyList">程序集</param>
        /// <param name="interfaceType">接口類別</param>
        /// <returns>實現了某個接口的的類列表</returns>
        public static List<Type> FindTypesByInterface(Type interfaceType)
        {
            return _GlobalAssemblyList.FindTypesByInterface(interfaceType);
        }
        #endregion

        #region 泛型類型的查找
        private static string GetFullGenericTypeNameString(string typeName
            , params string[] childTypeQualifiedNames)
        {
            if (childTypeQualifiedNames == null || childTypeQualifiedNames.Length == 0)
                return typeName;

            int childTypeCount = childTypeQualifiedNames.Length;
            string fullTypeName = typeName;
            fullTypeName += "`" + childTypeCount.ToString();

            string childTypeString = null;
            foreach (string childTypeQualifiedName in childTypeQualifiedNames)
            {
                if (childTypeString != null)
                    childTypeString += ",";

                childTypeString += "[" + childTypeQualifiedName + "]";
            }

            fullTypeName += "[" + childTypeString + "]";
            return fullTypeName;
        }

        /// <summary>
        /// 获取泛型类型
        /// </summary>
        public static Type GetGenericType(string assemblyName
            , string typeName
            , params string[] childTypeQualifiedNames)
        {
            string fullTypeName = GetFullGenericTypeNameString(typeName, childTypeQualifiedNames) + "," + assemblyName;

            return Type.GetType(fullTypeName);
        }

        public static Type GetGenericType(this Assembly assembly
            , string typeName
            , params string[] childTypeQualifiedNames)
        {
            string fullTypeName = GetFullGenericTypeNameString(typeName, childTypeQualifiedNames);
            return assembly.GetType(fullTypeName);
        }
        #endregion
    }
}
